<template>
  <div v-if="easyFlowVisible" class="flow-penal">

    <!--  流程图画布 -->
    <div id="efContainer" ref="efContainerRef" class="container" :data-zoom="canvasDataRoom"
      :style="canvasRoomScaleStyle" v-flowDrag>
      <template v-for="node in data.nodeList" :key="node.id">
        <flow-node @addNode="addNode" :id="node.id" :node="node" :activeElement="activeElement"
          @changeNodeSite="changeNodeSite" @clickNode="clickNode" @autoAddNode="autoAddNode"> </flow-node>
      </template>
      <!-- 给画布一个默认的宽度和高度 -->
      <div style="position: absolute; top: 80%; left: 70%">&nbsp;</div>
    </div>

    <!-- 右键 -->
    <!-- <div v-if="menuVisible" class="context-menu" :style="menuPosition">
      <a-button @click="deleteNode(activeElement.nodeId)">删除节点</a-button><br />
      <a-button @click="copyNode(activeElement.nodeId)">复制节点</a-button>
    </div> -->
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick, provide, defineEmits, defineExpose, watch, computed } from "vue";
import { message, Modal } from "ant-design-vue";
import lodash from "lodash";
import draggable from "vuedraggable";
import "@/components/flowDesign/styles/index.css";
import { jsPlumb } from "jsplumb";
import { jsplumbSetting, jsplumbConnectOptions, jsplumbSourceOptions, jsplumbTargetOptions } from "@/components/flowDesign/dicts/mixins.js";
import { nodeTypeNames } from "@/components/flowDesign/dicts/show.js";
import { isBranch, nodeLeft, getUUID, addConfig } from "@/components/flowDesign/dicts/utils.js";
import { ForceDirected } from "@/components/flowDesign/dicts/force-directed.js";
import flowNode from "@/components/flowDesign/flowNode/node.vue";
import { dataB } from "@/components/flowDesign/dicts/data_B.js";

const props = defineProps({
  flowData: {
    type: Object,
    default: dataB
  },
  showNodeMenu: {
    type: Boolean,
    default: false
  }
});
/**
 * 流程图
 */
const nodeFormRef = ref();
const efContainerRef = ref();
let _jsplumb = ref(null); // jsplumb 实例
const easyFlowVisible = ref(true); // 控制画布销毁
const flowInfoVisible = ref(false); // 控制流程数据显示与隐藏
const loadEasyFlowFinish = ref(false); // 是否加载完毕标志位
// 数据
const data = ref({
  nodeList: [],
  lineList: []
});
// 激活的元素、可能是节点、可能是连线
const activeElement = ref({
  nodeKey: undefined,
  type: undefined, // 可选值 node 、line
  nodeId: undefined, // 节点ID
  sourceId: undefined, // 连线ID
  targetId: undefined
});
onMounted(() => {
  _jsplumb.value = jsPlumb.getInstance(); //  创建jsPlumb实例
  dataReload(dataB); // 默认加载流程A的数据、在这里可以根据具体的业务返回符合流程数据格式的数据即可
});
const canvasDataRoom = ref(100);
const canvasRoomScaleStyle = computed(() => {
  return {
    transform: "scale(" + canvasDataRoom.value / 100 + ")"
  };
});


// 加载流程图
const dataReload = (dataSource) => {
  console.log(dataSource);
  easyFlowVisible.value = false;
  data.value.nodeList = [];
  data.value.lineList = [];
  nextTick(() => {
    easyFlowVisible.value = true;
    data.value = dataSource;
    nextTick(() => {
      _jsplumb.value = jsPlumb.getInstance();
      nextTick(() => {
        jsPlumbInit();
      });
    });
  });
};
const jsPlumbInit = () => {
  _jsplumb.value.ready(() => {
    _jsplumb.value.importDefaults(jsplumbSetting); // 导入默认配置
    _jsplumb.value.setSuspendDrawing(false, true); // 会使整个jsPlumb立即重绘。
    loadEasyFlow(); // 初始化节点
    // 单点击了连接线, https://www.cnblogs.com/ysx215/p/7615677.html
    _jsplumb.value.bind("click", (conn, originalEvent) => {
      activeElement.value.type = "line";
      activeElement.value.sourceId = conn.sourceId;
      activeElement.value.targetId = conn.targetId;
    });
    // 双击删除连线
    _jsplumb.value.bind("dblclick", (conn, originalEvent) => {
      _jsplumb.value.deleteConnection(conn);
    });

    // 连线
    _jsplumb.value.bind("connection", (evt) => {
      let from = evt.source.id;
      let to = evt.target.id;
      if (loadEasyFlowFinish.value) {
        data.value.lineList.push({
          from: from,
          to: to,
          fromKey: from,
          toKey: to
        });
      }
    });

    // 删除连线回调
    _jsplumb.value.bind("connectionDetached", (evt) => {
      deleteLine(evt.sourceId, evt.targetId);
    });
    // 改变线的连接节点
    _jsplumb.value.bind("connectionMoved", (evt) => {
      changeLine(evt.originalSourceId, evt.originalTargetId);
    });

    // 连线右击
    _jsplumb.value.bind("contextmenu", (evt) => { });

    // 连线
    _jsplumb.value.bind("beforeDrop", (evt) => {
      let from = evt.sourceId;
      let to = evt.targetId;
      if (from === to) {
        message.error("节点不支持连接自己");
        return false;
      }
      if (hasLine(from, to)) {
        message.error("该关系已存在,不允许重复创建");
        return false;
      }
      if (hashOppositeLine(from, to)) {
        message.error("不支持两个节点之间连线回环");
        return false;
      }
      if (hasLineFromNode(from, to)) {
        message.error("每个分支条件只能连接一个节点");
        return false;
      }
      message.success("连接成功");
      return true;
    });

    // beforeDetach
    // _jsplumb.value.bind("beforeDetach", (evt) => { });
    _jsplumb.value.setContainer(efContainerRef.value);
    // 添加拖拽功能
    _jsplumb.value.draggable(efContainer);
  });
};
// 加载默认流程图
const loadEasyFlow = () => {
  // 初始化节点
  if (data.value.nodeList) {
    for (var i = 0; i < data.value.nodeList.length; i++) {
      let node = data.value.nodeList[i];
      _jsplumb.value.makeSource(node.id, lodash.merge(jsplumbSourceOptions, {})); // 设置源点，可以拖出线连接其他节点
      _jsplumb.value.makeTarget(node.id, jsplumbTargetOptions); // 设置目标点，其他源点拖出的线可以连接该节点
      if (!node.viewOnly) {
        _jsplumb.value.draggable(node.id, {
          containment: "parent",
          stop: function (el) { }
        });
      }
    }
    // 初始化连线
    for (var i = 0; i < data.value.lineList.length; i++) {
      let line = data.value.lineList[i];
      var connParam = {
        source: line.from,
        target: line.to,
        label: line.label ? line.label : "",
        connector: line.connector ? line.connector : "",
        anchors: line.anchors ? line.anchors : undefined,
        paintStyle: line.paintStyle ? line.paintStyle : undefined
      };
      _jsplumb.value.connect(connParam, jsplumbConnectOptions);
    }
  }
  nextTick(() => {
    loadEasyFlowFinish.value = true;
  });
};
// 设置连线条件
const setLineLabel = (from, to, label) => {
  var conn = _jsplumb.value.getConnections({
    source: from,
    target: to
  })[0];
  if (!label || label === "") {
    conn.removeClass("flowLabel");
    conn.addClass("emptyFlowLabel");
  } else {
    conn.addClass("flowLabel");
  }
  conn.setLabel({
    label: label
  });
  data.value.lineList.forEach(function (line) {
    if (line.from == from && line.to == to) {
      line.label = label;
    }
  });
};
// 删除线
const deleteLine = (from, to) => {
  data.value.lineList = data.value.lineList.filter(function (line) {
    if (line.from == from && line.to == to) {
      return false;
    }
    return true;
  });
};
// 改变连线
const changeLine = (oldFrom, oldTo) => {
  deleteLine(oldFrom, oldTo);
};
// 改变节点的位置
const changeNodeSite = (nodeData) => {
  for (var i = 0; i < data.value.nodeList.length; i++) {
    let node = data.value.nodeList[i];
    if (node.id === nodeData.nodeId) {
      node.left = nodeData.left;
      node.top = nodeData.top;
    }
  }
};
// 点击节点
const clickNode = (node) => {
  menuVisible.value = true;
  // 设置菜单位置
  menuPosition.value.top = `${event.clientY + 30}px`;
  menuPosition.value.left = `${event.clientX + 60}px`;

  activeElement.value.type = "node";
  activeElement.value.nodeKey = node.nodeKey;
  activeElement.value.nodeId = node.nodeKey;
  if (node.nodeType == nodeTypeNames.parallelBranchNode || node.nodeType == nodeTypeNames.ifBranchNode) {
    autoAddNode(node);
  } else if (node.nodeType == nodeTypeNames.finishNode) {
    return;
  } else {
    emit("clickNodeId", node.nodeKey);
  }
};
/**
 * 拖拽结束后添加新的节点
 * @param evt
 * @param nodeMenu 被添加的节点对象
 * @param mousePosition 鼠标拖拽结束的坐标
 */
const addNode = (evt, nodeMenu, mousePosition) => {
  var screenX = evt.originalEvent.clientX,
    screenY = evt.originalEvent.clientY;
  let efContainer = efContainerRef.value;
  var containerRect = efContainer.getBoundingClientRect();
  var left = screenX,
    top = screenY;
  // 计算是否拖入到容器中
  if (left < containerRect.x || left > containerRect.width + containerRect.x || top < containerRect.y || containerRect.y > containerRect.y + containerRect.height) {
    message.error("请把节点拖入到画布中");
    return;
  }
  left = left - containerRect.x + efContainer.scrollLeft;
  top = top - containerRect.y + efContainer.scrollTop;
  // 居中
  left -= 85;
  top -= 16;
  var nodeId = getUUID();
  // 动态生成名字
  var origName = nodeMenu.name;
  var nodeName = origName;
  var node = {
    id: nodeId,
    nodeName: nodeName,
    nodeKey: nodeId, //节点Key
    nodeType: nodeMenu.nodeType, //节点类型
    left: isBranch(nodeMenu.nodeType) ? left + 80 + "px" : left + "px",
    top: top + "px",
    name: nodeName,
    type: nodeMenu.type,
    ico: nodeMenu.ico,
    state: "success",
    config: addConfig(nodeMenu.nodeType)
  };
  /**
   * 这里可以进行业务判断、是否能够添加该节点
   */
  data.value.nodeList.push(node);
  if (node.nodeType == nodeTypeNames.parallelBranchNode || node.nodeType == nodeTypeNames.ifBranchNode) {
    addNodeTwo(node, left, top);
  }
  nextTick(function () {
    _jsplumb.value.makeSource(nodeId, jsplumbSourceOptions);
    _jsplumb.value.makeTarget(nodeId, jsplumbTargetOptions);
    _jsplumb.value.draggable(nodeId, {
      containment: "parent",
      stop: function (el) { }
    });
  });
};
// 添加两个子节点
const addNodeTwo = (node, left, top) => {
  var code = getUUID();
  for (let i = 0; i < 2; i++) {
    var nodeId = getUUID();
    let nodes = {
      id: nodeId,
      nodeName: i == 0 ? "条件1" : "其他情况",
      nodeType: node.nodeType === nodeTypeNames.parallelBranchNode ? nodeTypeNames.pifNode : nodeTypeNames.ifNode, //节点Key
      nodeKey: nodeId, //节点类型
      left: nodeLeft(i, left),
      code: code, //节点编码
      top: top + 130 + "px",
      name: i == 0 ? "条件1" : "其他情况",
      ico: "",
      state: "success",
      config: addConfig(node.nodeType)
    };
    data.value.nodeList.push(nodes);
    let lines = {
      from: node.nodeKey,
      fromKey: node.nodeKey,
      to: nodes.nodeKey,
      toKey: nodes.nodeKey
    };
    let connParam = {
      source: node.nodeKey,
      target: nodes.nodeKey
    };
    nextTick(function () {
      _jsplumb.value.makeSource(nodes.id, jsplumbSourceOptions);
      _jsplumb.value.makeTarget(nodes.id, jsplumbTargetOptions);
      _jsplumb.value.draggable(nodes.id, {
        containment: "parent",
        stop: function (el) { }
      });
      _jsplumb.value.connect(connParam, jsplumbConnectOptions);
    });
  }
};
//自动添加节点
const autoAddNode = (node) => {
  console.log("自动添加节点", node);
};
/**
 * 删除节点/复制节点
 * @param nodeId 被删除节点的ID
 * @param copyNodeId 被复制节点的ID
 */
const deleteNode = (nodeId) => {
  Modal.confirm({
    title: "提示",
    content: `确定要删除节点${nodeId}?`,
    async onOk() {
      /**
       * 这里需要进行业务判断，是否可以删除
       */
      data.value.nodeList = data.value.nodeList.filter(function (node) {
        if (node.id === nodeId) {
          // 伪删除，将节点隐藏，否则会导致位置错位
          // node.show = false
          return false;
        }
        return true;
      });
      nextTick(function () {
        _jsplumb.value.removeAllEndpoints(nodeId);
      });
      menuVisible.value = false;
    }
  });
  return true;
};
const copyNode = (copyNodeId) => {
  console.log("复制节点", copyNodeId);
};

// 是否具有该线
const hasLine = (from, to) => {
  for (var i = 0; i < data.value.lineList.length; i++) {
    var line = data.value.lineList[i];
    if (line.from === from && line.to === to) {
      return true;
    }
  }
  return false;
};
// 是否含有相反的线
const hashOppositeLine = (from, to) => {
  return hasLine(to, from);
};
// 条件分支节点是否已经连接其他节点
const hasLineFromNode = (data, from, to) => { };
/**
 * 右键
 */
const menuVisible = ref(false);
const menuPosition = ref({
  top: 0,
  left: 0
});
const hide = () => {
  activeElement.value.nodeId = null;
  menuVisible.value = false;
};
/**
 * 缩放
 * */
const baseZoom = ref(1);
const cutSize = (val) => {
  if (val == "enlarge") {
    baseZoom.value += 0.25;
  } else {
    baseZoom.value -= 0.25;
  }
  const zoom = baseZoom.value;
  efContainerRef.value.style.transform = `scale(${zoom})`;
  efContainerRef.value.style.WebkitTransform = `scale(${zoom})`;
  efContainerRef.value.style.MozTransform = `scale(${zoom})`;
  efContainerRef.value.style.MsTransform = `scale(${zoom})`;
  efContainerRef.value.style.OTransform = `scale(${zoom})`;
  _jsplumb.value.setZoom(zoom);
};
defineExpose({
  cutSize,
  baseZoom
});
</script>
<style lang="less" scoped>
.flow-penal {
  height: calc(100vh);
  widows: calc(100vw);

  .container {
    height: calc(100% - 45px);
    width: 100%;
  }

  .flow-content {
    height: 100%;
    width: 100%;
  }

  .context-menu {
    position: fixed;
    background-color: #fff;
    // border: 1px solid #ccc;
    // padding: 10px;
    z-index: 1000;
    /* 确保菜单在最上层 */
  }
}
</style>
